/**
 * Copyright &copy; 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.sdkj.dspservice.common.base.controller;

import com.sdkj.dspservice.common.base.entity.BaseResult;
import com.sdkj.dspservice.common.constant.HttpStatus;
import com.sdkj.dspservice.common.utils.BeanValidators;
import com.sdkj.dspservice.common.utils.DateUtils;
import com.sdkj.dspservice.common.utils.JsonUtils;
import com.sdkj.dspservice.common.utils.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * 控制器支持类
 * @author tm
 * @version 2020-11-23
 */
public abstract class BaseController {

	@Value(value = "project-config.apiUrl")
	protected String apiPath;
	@Value(value = "project-config.version")
	protected String version;

	/**
	 * 日志对象
	 */
	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 验证Bean实例对象
	 */
	@Autowired
	protected Validator validator;

	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(Model model, Object object, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
			list.add(0, "操作失败：");
			addMessage(model, list.toArray(new String[]{}));
			return false;
		}
		return true;
	}

	/**
	 * 服务端参数有效性验证
	 * @param baseResult
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(HttpServletResponse response, BaseResult baseResult, Object object, Class<?>... groups) {
		return beanValidator(response, baseResult, object, "数据验证失败：", groups);
	}

	/**
	 * 服务端参数有效性验证
	 * @param response
	 * @param baseResult
	 * @param object 验证的实体对象
	 * @param errorMsgHead 错误信息的头部
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(HttpServletResponse response, BaseResult baseResult, Object object, String errorMsgHead, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex);
			response.setStatus(HttpStatus.STATUS_400);
			baseResult.setMsg(errorMsgHead + StringUtils.join(list, "，"));
			return false;
		}
		return true;
	}

	/**
	 * 服务端参数有效性验证
	 * @param response
	 * @param baseResult
	 * @param parameters 验证的参数对象
	 * @param errorMessages 错误提示（按parameters顺序）
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(HttpServletResponse response, BaseResult baseResult, Object[] parameters, String[] errorMessages) {
		boolean result = true;
		for(int i = 0; i < parameters.length; ++i){
			Object parameter = parameters[i];
			boolean isError;
			// 判断参数是否错误
			if(parameter instanceof String){
				String parameterStr = (String) parameter;
				isError = StringUtils.isBlank(parameterStr);
			}else {
				isError = parameter == null;
			}
			// 参数错误时的操作
			if(isError){
				result = false;
				response.setStatus(HttpStatus.STATUS_400);
				// 添加错误消息
				baseResult.setErrorMessage(StringUtils.join("操作成功".equals(baseResult.getMsg()) ? "" : baseResult.getMsg(), errorMessages[i]));
			}
		}
		return result;
	}
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组，不传入此参数时，同@Valid注解验证
	 * @return 验证成功：继续执行；验证失败：抛出异常跳转400页面。
	 */
	protected void beanValidator(Object object, Class<?>... groups) {
		BeanValidators.validateWithException(validator, object, groups);
	}

	/**
	 * 添加Model消息
	 * @param model
	 * @param messages
	 */
	protected void addMessage(Model model, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages){
			sb.append(message).append(messages.length>1?"<br/>":"");
		}
		if(model instanceof RedirectAttributes){
			RedirectAttributes redirectAttributes = (RedirectAttributes) model;
			redirectAttributes.addFlashAttribute("message", sb.toString());
		}else {
			model.addAttribute("message", sb.toString());
		}
	}
	
	/**
	 * 客户端返回JSON字符串
	 * @param response
	 * @param object
	 * @return
	 */
	protected String renderString(HttpServletResponse response, Object object) {
		return renderString(response, JsonUtils.toJsonString(object), "application/json");
	}
	
	/**
	 * 客户端返回字符串
	 * @param response
	 * @param string
	 * @return
	 */
	protected String renderString(HttpServletResponse response, String string, String type) {
		try {
			response.reset();
	        response.setContentType(type);
	        response.setCharacterEncoding("utf-8");
			response.getWriter().print(string);
			return null;
		} catch (IOException e) {
			return null;
		}
	}

//	/**
//	 * 参数绑定异常
//	 */
//	@ExceptionHandler({BindException.class, ConstraintViolationException.class, ValidationException.class})
//    public String bindException() {
//        return "error/400";
//    }
	
	/**
	 * 授权登录异常
	 */
	@ExceptionHandler({AuthenticationException.class})
    public String authenticationException() {  
        return "error/403";
    }
	
	/**
	 * 初始化数据绑定
	 * 1. 将所有传递进来的String进行HTML编码，防止XSS攻击
	 * 2. 将字段中Date类型转换为String类型
	 */
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		// String类型转换，将所有传递进来的String进行HTML编码，防止XSS攻击
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value != null ? value.toString() : "";
			}
		});
		// Date 类型转换
		binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(DateUtils.parseDate(text));
			}
		});
	}
	
}
